/*
 * This file is part of the openHiTLS project.
 *
 * openHiTLS is licensed under the Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *
 *     http://license.coscl.org.cn/MulanPSL2
 *
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */

#include "app_asn1parse.h"

static int print_hex(BSL_UIO *outUio, const uint8_t *buf, uint32_t len) {
    int ret = HITLS_APP_SUCCESS;
    ret = AppPrint(outUio, ":");
    if (ret) {   
        BSL_ERR_PUSH_ERROR(ret);
        return ret;
    }

    for (uint32_t i = 0; i < len; ++i) {
        ret = AppPrint(outUio, "%02X", buf[i]);
        if (ret) {   
            BSL_ERR_PUSH_ERROR(ret);
            return ret;
        }
    }
    return HITLS_APP_SUCCESS;
}

static int print_obstring(BSL_UIO *outUio, uint8_t *buff, uint32_t len){
    int ret = HITLS_APP_SUCCESS;
    char p[len+2];
    snprintf(p, len+2, ":%s", buff);
    ret = AppPrint(outUio, p);
    if (ret != HITLS_APP_SUCCESS) {
        BSL_ERR_PUSH_ERROR(ret);
        return ret;
    }
    return HITLS_APP_SUCCESS;
}

static int print_oid(BSL_UIO *outUio,uint8_t *buff, uint32_t len){
    const uint8_t *p = (const uint8_t *)buff;
    uint32_t n = len;
    uint32_t i = 0;
    unsigned first = p[i++];
    unsigned x = first / 40;
    unsigned y = first % 40;
    int ret = HITLS_APP_SUCCESS;

    if (x > 2) { x = 2; y = first - 80; }
    ret = AppPrint(outUio, ":%u.%u", x, y);
    if (ret) {   
        BSL_ERR_PUSH_ERROR(ret);
        return ret;
    }
    while (i < n) {
        uint64_t v = 0;
        int cont = 1;
        do {
            uint8_t b = p[i++];
            v = (v << 7) | (b & 0x7Fu);
            cont = (b & 0x80u) != 0;
            if (i > n) return 0; 
        } while (cont);
        ret = AppPrint(outUio, ".%llu", (unsigned long long)v);
        if (ret) {   
            BSL_ERR_PUSH_ERROR(ret);
            return ret;
        }
    }
    return HITLS_APP_SUCCESS;
}

static int print_bmpstring(BSL_UIO *outUio, uint8_t *buff, uint32_t len) {
    const uint8_t *p = (const uint8_t *)buff;
    uint32_t n = len;
    int ret = HITLS_APP_SUCCESS;

    AppPrint(outUio, ":");
    for (uint32_t i = 0; i < n; i += 2) {
        uint16_t ch = (p[i] << 8) | p[i + 1];
        ret = AppPrint(outUio, "%c", (ch <= 0x7F) ? (char)ch : '?');
        if (ret) {   
            BSL_ERR_PUSH_ERROR(ret);
            return ret;
        }
    }

    return HITLS_APP_SUCCESS;
}

static int print_time(BSL_UIO *outUio, uint8_t *buff, uint32_t len) {
    /*250923165708Z*/
    /*Sep 23 16:57:08 2025 GMT*/
    (void)len;
    const char months[12][4] = {
    "Jan", "Feb", "Mar", "Apr", "May", "Jun", 
    "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
    };
    int ret = HITLS_APP_SUCCESS;

    AppPrint(outUio, "%c%c%c %c%c %c%c:%c%c:%c%c 20%c%c GMT",
             months[(buff[2] - '0') * 10 + (buff[3] - '0') - 1][0],
             months[(buff[2] - '0') * 10 + (buff[3] - '0') - 1][1],
             months[(buff[2] - '0') * 10 + (buff[3] - '0') - 1][2],
             buff[4], buff[5],
             buff[6], buff[7],
             buff[8], buff[9],
             buff[10], buff[11],
             buff[0], buff[1]);

    if (ret) {   
        BSL_ERR_PUSH_ERROR(ret);
        return ret;
    }

    return ret;
}

static int asn_print_fsname(BSL_UIO *out, int indent,
                             const char *fname){
    int ret = HITLS_APP_SUCCESS;

    ret = AppPrint(out, "%*s%s", indent, "", fname); 
    if (ret != HITLS_APP_SUCCESS) {
        BSL_ERR_PUSH_ERROR(ret);
        return ret;
    }

    return ret;
}

int32_t Asn1ParseInfo(BSL_UIO *outUio, uint32_t offset, uint32_t depth, uint32_t indent, uint32_t hl, uint32_t len,
                           const char* p, const char* tname) {

    int ret = HITLS_APP_SUCCESS; 
    char chr[64];

    snprintf(chr, sizeof(chr), "%5u:d=%-2u hl=%u l=%4u %s: ", offset, depth, hl, len, p);
    depth = indent ? depth : 0;
    ret = AppPrint(outUio, "%s%*s%-*s", chr, depth, "", 18-depth, tname); 
    if (ret) {   
        BSL_ERR_PUSH_ERROR(ret);
        return ret;
    }
    return ret;
}

int32_t Asn1ParseFormat(BSL_UIO *outUio, BSL_ASN1_Buffer *buff, const char* fname,uint32_t dlimit, uint32_t indent, uint32_t dump) {
    uint8_t *data = buff->buff;
    uint32_t len = buff->len > dlimit ? dlimit : buff->len;
    uint8_t tag = buff->tag;
    int ret = HITLS_APP_SUCCESS;

    if (fname) {
        ret = asn_print_fsname(outUio, indent, fname);
        if (ret != HITLS_APP_SUCCESS) {
            return ret;
        }
    }

    switch(tag){
        case BSL_ASN1_TAG_NULL:
        case BSL_ASN1_TAG_RALATIVE_ID:
        case BSL_ASN1_TAG_SEQUENCE:
        case BSL_ASN1_TAG_SET:
            break;
        case BSL_ASN1_TAG_BOOLEAN:   
            uint8_t bol=data[0];
            ret = AppPrint(outUio, bol?":True":":False");
            break;

        case BSL_ASN1_TAG_ENUMERATED:
        case BSL_ASN1_TAG_INTEGER:
            ret = print_hex(outUio,data,len);
            break;

        case BSL_ASN1_TAG_BITSTRING:
            if (dump) {
                AppPrint(outUio, "[HEX DUMP]");
                ret=print_hex(outUio, data, len);
            }
            break;
        case BSL_ASN1_TAG_OCTETSTRING:
            AppPrint(outUio, "[HEX DUMP]");
            ret=print_hex(outUio, data, len);
            break;

        case BSL_ASN1_TAG_OBJECT_DESCP:
        case BSL_ASN1_TAG_OBJECT_ID:
            ret =print_oid(outUio, data, len);
            break;
        case BSL_ASN1_TAG_REAL:
            AppPrint(outUio, "<REAL unsupported>",indent);
            break;
        case BSL_ASN1_TAG_EMBEDDED_PDV:
            AppPrint(outUio, "<EMBEDDED PDV unsupported>",indent);
            break;
        case BSL_ASN1_TAG_INSTANCE_OF:
            AppPrint(outUio, "<INSTANCE OF unsupported>",indent);
            break;

        case BSL_ASN1_TAG_GENERALIZEDTIME:
        case BSL_ASN1_TAG_TIME:
        case BSL_ASN1_TAG_UTCTIME:
            ret=print_time(outUio, data, len);
            break;
            
        case BSL_ASN1_TAG_PRINTABLESTRING:
        case BSL_ASN1_TAG_UTF8STRING:
        case BSL_ASN1_TAG_IA5STRING:
            ret=print_obstring(outUio,data,len);
            break;
        case BSL_ASN1_TAG_BMPSTRING:
            ret=print_bmpstring(outUio, data, len);
            break;
        default:
            AppPrintError("Unknown tag: %d\n", tag);
    }
    return ret;
}
